package org.hotswap.agent.plugin.remote;

import org.hotswap.agent.annotation.OnClassLoadEvent;
import org.hotswap.agent.annotation.Plugin;
import org.hotswap.agent.javassist.CannotCompileException;
import org.hotswap.agent.javassist.ClassPool;
import org.hotswap.agent.javassist.CtClass;
import org.hotswap.agent.javassist.CtMethod;
import org.hotswap.agent.logging.AgentLogger;
import org.hotswap.agent.watch.remote.Base64Util;

import java.io.ByteArrayInputStream;
import java.lang.reflect.Method;

/**
 * @author luzhiqing
 * @version 1.0
 * @date 2022/11/16
 */
@Plugin(name = "ControllerRegister",
        description = "add controller definition/change.",
        testedVersions = {"All between 3.0.1 - 5.2.2"},
        expectedVersions = {"3x", "4x", "5x"})
public class ControllerRegister {
    protected static AgentLogger LOGGER = AgentLogger.getLogger(ControllerRegister.class);

    @OnClassLoadEvent(classNameRegexp = "org.springframework.boot.loader.MainMethodRunner")
    public static void registerController(CtClass clazz) {
        LOGGER.debug("register RemoteController");
        try {
            CtMethod run = clazz.getDeclaredMethod("run");
            run.insertAfter("org.hotswap.agent.plugin.remote.ControllerRegister.newRemoteControllerClass(Thread.currentThread().getContextClassLoader());");
        } catch (Exception e) {
            LOGGER.debug("register RemoteController fail", e);
        }
    }

    /**
     * 增强ClassPathBeanDefinitionScanner#doScan方法
     *
     * @param clazz
     * @see org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan(String...)
     */
    @OnClassLoadEvent(classNameRegexp = "org.springframework.context.annotation.ClassPathBeanDefinitionScanner")
    public static void registerControllerPackage(CtClass clazz) {
        LOGGER.debug("register ControllerPackage");
        try {

            CtMethod run = clazz.getDeclaredMethod("doScan");
            String codeStr =
                    "        boolean hasSpecialPackage = false;\n" +
                    "        String packageName = \"org.hotswap.agent.watch.remote\";\n" +
                    "        java.util.List reBasePackages = new java.util.ArrayList();\n" +
                    "\n" +
                    "        for(int i = 0; i < basePackages.length; ++i) {\n" +
                    "            String basePackage = basePackages[i];\n" +
                    "            if (packageName.equals(basePackage)) {\n" +
                    "                hasSpecialPackage = true;\n" +
                    "            }\n" +
                    "\n" +
                    "            reBasePackages.add(basePackage);\n" +
                    "        }\n" +
                    "\n" +
                    "        if (!hasSpecialPackage) {\n" +
                    "            reBasePackages.add(packageName);\n" +
                    "        }\n" +
                    "\n" +
                    "        basePackages = (String[])((String[])reBasePackages.toArray(new String[0]));"

                    ;
            run.insertBefore(codeStr);
        } catch (Exception e) {
            LOGGER.debug("register RemoteController fail", e);
        }
    }

    public static void newRemoteControllerClass(ClassLoader classLoader) {
        ClassPool cp = new ClassPool();
        CtClass ctClass = null;
        byte[] decode = null;
        try {
            String str = "yv66vgAAADQA3woAGwB1CgB2AHcKAHgAeQcAegoABAB1CgB7AHwKAAQAfQoAdgB+CgAEAH8KAAQAgAoABACBCgB2AIIKADEAgwoABACECgAxAIUIAIYKACMAhwgAiAoAIwCJCACKCACLCgAjAIwKAI0AjgsAbQCPCQAxAJAIAJEHAJIKAJMAlAsAbQCVCwBuAJYLAG8AlwsAbwCYBwCZCwAhAJoHAJsIAJwLACEAnQcAngoABACCCgAjAJ8LACYAlgcAoAsAKgChBwCiCACjCgCTAKQIAKUKAJMApgcApwoAkwCoAQAGTE9HR0VSAQAnTG9yZy9ob3Rzd2FwL2FnZW50L2xvZ2dpbmcvQWdlbnRMb2dnZXI7AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBADFMb3JnL2hvdHN3YXAvYWdlbnQvd2F0Y2gvcmVtb3RlL1JlbW90ZUNvbnRyb2xsZXI7AQAGZGVwbG95AQBEKExvcmcvaG90c3dhcC9hZ2VudC93YXRjaC9yZW1vdGUvUmVtb3RlRGVwbG95Q21kOylMamF2YS9sYW5nL1N0cmluZzsBAAlkZXBsb3lDbWQBADBMb3JnL2hvdHN3YXAvYWdlbnQvd2F0Y2gvcmVtb3RlL1JlbW90ZURlcGxveUNtZDsBAA9jbGFzc0RlZmluaXRpb24BABJMamF2YS9sYW5nL1N0cmluZzsBAAZkZWNvZGUBAAJbQgEABWV2ZW50AQAoTG9yZy9ob3Rzd2FwL2FnZW50L3dhdGNoL1dhdGNoRmlsZUV2ZW50OwEAC2NvbnZlcnRQYXRoAQAZUnVudGltZVZpc2libGVBbm5vdGF0aW9ucwEAOExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9iaW5kL2Fubm90YXRpb24vUmVxdWVzdE1hcHBpbmc7AQAEcGF0aAEABy9kZXBsb3kBAAZtZXRob2QBADdMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL1JlcXVlc3RNZXRob2Q7AQAEUE9TVAEAIlJ1bnRpbWVWaXNpYmxlUGFyYW1ldGVyQW5ub3RhdGlvbnMBADVMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL1JlcXVlc3RCb2R5OwEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAHY2xhc3NlcwEAE1tMamF2YS9sYW5nL1N0cmluZzsBAA1TdGFja01hcFRhYmxlBwBSAQATY2FsbExpc3RlbmVyc1JlbW90ZQEAKyhMb3JnL2hvdHN3YXAvYWdlbnQvd2F0Y2gvV2F0Y2hGaWxlRXZlbnQ7KVYBAAV2YXIxMQEAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwEACGxpc3RlbmVyAQAsTG9yZy9ob3Rzd2FwL2FnZW50L3dhdGNoL1dhdGNoRXZlbnRMaXN0ZW5lcjsBAANrZXkBABN3YXRjaEV2ZW50TGlzdGVuZXJzAQAQTGphdmEvdXRpbC9MaXN0OwEABWVudHJ5AQAFRW50cnkBAAxJbm5lckNsYXNzZXMBABVMamF2YS91dGlsL01hcCRFbnRyeTsBAAlsaXN0ZW5lcnMBAA9MamF2YS91dGlsL01hcDsBAAVtYXRjaAEAAVoBAAdlbnRyaWVzAQAPTGphdmEvdXRpbC9TZXQ7AQAWTG9jYWxWYXJpYWJsZVR5cGVUYWJsZQEAPkxqYXZhL3V0aWwvTGlzdDxMb3JnL2hvdHN3YXAvYWdlbnQvd2F0Y2gvV2F0Y2hFdmVudExpc3RlbmVyOz47AQBnTGphdmEvdXRpbC9NYXAkRW50cnk8TGphdmEvbGFuZy9TdHJpbmc7TGphdmEvdXRpbC9MaXN0PExvcmcvaG90c3dhcC9hZ2VudC93YXRjaC9XYXRjaEV2ZW50TGlzdGVuZXI7Pjs+OwEAYUxqYXZhL3V0aWwvTWFwPExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL3V0aWwvTGlzdDxMb3JnL2hvdHN3YXAvYWdlbnQvd2F0Y2gvV2F0Y2hFdmVudExpc3RlbmVyOz47PjsBAHhMamF2YS91dGlsL1NldDxMamF2YS91dGlsL01hcCRFbnRyeTxMamF2YS9sYW5nL1N0cmluZztMamF2YS91dGlsL0xpc3Q8TG9yZy9ob3Rzd2FwL2FnZW50L3dhdGNoL1dhdGNoRXZlbnRMaXN0ZW5lcjs+Oz47PjsHAKkHAKoHAKsBAAg8Y2xpbml0PgEAClNvdXJjZUZpbGUBABVSZW1vdGVDb250cm9sbGVyLmphdmEBADhMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL1Jlc3RDb250cm9sbGVyOwEACC9ob3Rzd2FwDAA1ADYHAKwMAK0ArgcArwwAQgCwAQAmb3JnL2hvdHN3YXAvYWdlbnQvd2F0Y2gvV2F0Y2hGaWxlRXZlbnQHALEMALIAswwAtAC1DAC2ALcMALgAuQwAugC7DAC8AL0MAL4ArgwARgBQDAC/AMAMAFUAVgEAATAMAMEAwgEACGNsYXNzZXMvDADDAMQBAAEvAQABLgwAxQDGBwDHDADIAMkMAMoAywwAMwA0AQAP55uR5ZCs5Zmo5Li656m6AQAQamF2YS9sYW5nL09iamVjdAcAzAwAzQDODADPANAMANEA0gwA0wDLDADUANUBABNqYXZhL3V0aWwvTWFwJEVudHJ5DADWANUBABBqYXZhL2xhbmcvU3RyaW5nAQAHcGF0aDp7fQwA1wDVAQAOamF2YS91dGlsL0xpc3QMANgA2QEAKm9yZy9ob3Rzd2FwL2FnZW50L3dhdGNoL1dhdGNoRXZlbnRMaXN0ZW5lcgwA2gBWAQATamF2YS9sYW5nL1Rocm93YWJsZQEABuW8guW4uAwA2wDcAQAqTm8gbWF0Y2ggZm9yICB3YXRjaCBldmVudCAne30nLCAgcGF0aCAne30nDADbAM4BAC9vcmcvaG90c3dhcC9hZ2VudC93YXRjaC9yZW1vdGUvUmVtb3RlQ29udHJvbGxlcgwA3QDeAQANamF2YS91dGlsL01hcAEADWphdmEvdXRpbC9TZXQBABJqYXZhL3V0aWwvSXRlcmF0b3IBAC5vcmcvaG90c3dhcC9hZ2VudC93YXRjaC9yZW1vdGUvUmVtb3RlRGVwbG95Q21kAQASZ2V0Q2xhc3NEZWZpbml0aW9uAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAClvcmcvaG90c3dhcC9hZ2VudC93YXRjaC9yZW1vdGUvQmFzZTY0VXRpbAEAFihMamF2YS9sYW5nL1N0cmluZzspW0IBABFqYXZhL2xhbmcvQm9vbGVhbgEAB3ZhbHVlT2YBABYoWilMamF2YS9sYW5nL0Jvb2xlYW47AQAJc2V0RGVsZXRlAQAWKExqYXZhL2xhbmcvQm9vbGVhbjspVgEADGdldEV2ZW50VHlwZQEAKigpTG9yZy9ob3Rzd2FwL2FnZW50L2Fubm90YXRpb24vRmlsZUV2ZW50OwEADHNldEV2ZW50VHlwZQEAKyhMb3JnL2hvdHN3YXAvYWdlbnQvYW5ub3RhdGlvbi9GaWxlRXZlbnQ7KVYBAAxzZXREaXJlY3RvcnkBAAQoWilWAQASc2V0Q2xhc3NEZWZpbml0aW9uAQAFKFtCKVYBAAdnZXRQYXRoAQAHc2V0UGF0aAEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABmxlbmd0aAEAAygpSQEABXNwbGl0AQAnKExqYXZhL2xhbmcvU3RyaW5nOylbTGphdmEvbGFuZy9TdHJpbmc7AQAHcmVwbGFjZQEARChMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTtMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspTGphdmEvbGFuZy9TdHJpbmc7AQAxb3JnL2hvdHN3YXAvYWdlbnQvd2F0Y2gvV2F0Y2hFdmVudExpc3RlbmVyTWFuYWdlcgEADGdldExpc3RlbmVycwEAESgpTGphdmEvdXRpbC9NYXA7AQAHaXNFbXB0eQEAAygpWgEAJW9yZy9ob3Rzd2FwL2FnZW50L2xvZ2dpbmcvQWdlbnRMb2dnZXIBAARpbmZvAQAoKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL09iamVjdDspVgEACGVudHJ5U2V0AQARKClMamF2YS91dGlsL1NldDsBAAhpdGVyYXRvcgEAFigpTGphdmEvdXRpbC9JdGVyYXRvcjsBAAdoYXNOZXh0AQAEbmV4dAEAFCgpTGphdmEvbGFuZy9PYmplY3Q7AQAGZ2V0S2V5AQAIZ2V0VmFsdWUBAApzdGFydHNXaXRoAQAVKExqYXZhL2xhbmcvU3RyaW5nOylaAQAHb25FdmVudAEABWVycm9yAQA9KExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvVGhyb3dhYmxlO1tMamF2YS9sYW5nL09iamVjdDspVgEACWdldExvZ2dlcgEAOihMamF2YS9sYW5nL0NsYXNzOylMb3JnL2hvdHN3YXAvYWdlbnQvbG9nZ2luZy9BZ2VudExvZ2dlcjsAIQAxABsAAAABAAwAMwA0AAAABQABADUANgABADcAAAAzAAEAAQAAAAUqtwABsQAAAAIAOAAAAAoAAgAAABgABAAZADkAAAAMAAEAAAAFADoAOwAAAAEAPAA9AAMANwAAAM8AAgAGAAAASyu2AAJNLLgAA067AARZtwAFOgQZBAO4AAa2AAcZBCu2AAi2AAkZBAO2AAoZBC22AAsqK7YADLcADToFGQQZBbYADioZBLcADxIQsAAAAAIAOAAAAC4ACwAAACAABQAhAAoAIgATACMAHAAkACUAJQArACYAMQAnADsAKABCACkASAAqADkAAAA+AAYAAABLADoAOwAAAAAASwA+AD8AAQAFAEYAQABBAAIACgBBAEIAQwADABMAOABEAEUABAA7ABAARgBBAAUARwAAABgAAQBIAAIASVsAAXMASgBLWwABZQBMAE0ATgAAAAcBAAEATwAAAAIARgBQAAEANwAAAIsAAwADAAAAKQErpQALAyu2ABGgAAUrsCsSErYAE00DLL6gAAUrsCwEMhIUEhW2ABawAAAAAwA4AAAAGgAGAAAANAANADUADwA3ABYAOAAcADkAHgA7ADkAAAAgAAMAAAApADoAOwAAAAAAKQBJAEEAAQAWABMAUQBSAAIAUwAAAAoAAw0B/AAOBwBUAAIAVQBWAAEANwAAAmkABgAMAAAA2rgAF00BLKUADCy5ABgBAJkAD7IAGRIaA70AG7YAHAM+LLkAHQEAOgQZBLkAHgEAOgUZBbkAHwEAmQCGGQW5ACABAMAAIToGGQa5ACIBAMAAIzoHsgAZEiQEvQAbWQMZB1O2ABwZBrkAJQEAwAAmOggrtgAnGQe2ACiZAEIEPhkIuQApAQA6CRkJuQAfAQCZAC0ZCbkAIAEAwAAqOgoZCiu5ACsCAKcAEzoLsgAZEi0ZCwO9ABu2AC6n/8+n/3YdmgAasgAZEi8FvQAbWQMrU1kEK7YAJ1O2ADCxAAEAnQClAKgALAAEADgAAABWABUAAAA/AAQAQAASAEEAHgBDACAARAAoAEUARwBGAFMARwBkAEgAcABJAHwASgB+AEsAnQBNAKUAUACoAE4AqgBPALgAUQC7AFMAvgBUAMIAVQDZAFcAOQAAAGYACgCqAA4AVwBYAAsAnQAbAFkAWgAKAFMAaABbAEEABwBwAEsAXABdAAgARwB0AF4AYQAGAAAA2gA6ADsAAAAAANoARABFAAEABADWAGIAYwACACAAugBkAGUAAwAoALIAZgBnAAQAaAAAACoABABwAEsAXABpAAgARwB0AF4AagAGAAQA1gBiAGsAAgAoALIAZgBsAAQAUwAAAH0ACfwAEgcAbQv+ABIBBwBuBwBv/wBVAAoHADEHAAQHAG0BBwBuBwBvBwAhBwAjBwAmBwBvAAD/ACAACwcAMQcABAcAbQEHAG4HAG8HACEHACMHACYHAG8HACoAAQcALPoAD/8AAgAGBwAxBwAEBwBtAQcAbgcAbwAA+gACGgAIAHAANgABADcAAAAhAAEAAAAAAAkSMbgAMrMAGbEAAAABADgAAAAGAAEAAAAWAAMAcQAAAAIAcgBHAAAAEgACAHMAAABIAAEASVsAAXMAdABgAAAACgABACEAbQBfBgk=";
            decode = Base64Util.decode(str);
            ctClass = cp.makeClass(new ByteArrayInputStream(decode));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        Class<?> clazz = null;
        try {
            clazz = Class.forName("org.hotswap.agent.plugin.spring.scanner.ClassPathBeanDefinitionScannerAgent", true, classLoader);
        } catch (ClassNotFoundException e) {
            LOGGER.debug("register RemoteController fail", e);
            return;
        }

        try {
            Class<?> aClass = ctClass.toClass(classLoader, classLoader.getClass().getProtectionDomain());
            LOGGER.info("load remote controller success");
        } catch (CannotCompileException e) {
            LOGGER.debug("register RemoteController fail", e);
        }


        Method method = null;
        try {
            method = clazz.getDeclaredMethod("refreshClass", String.class, byte[].class);
        } catch (NoSuchMethodException e) {
            LOGGER.debug("register RemoteController fail", e);
            return;
        }
        try {
            method.invoke(null, "org.hotswap.agent.watch.remote", decode);
        } catch (Exception e) {
            LOGGER.debug("register RemoteController fail", e);
        }
    }

}
